/**
 * 
 */
package com.thinkgsp.jxc.service.impl;

import java.math.BigDecimal;
import java.sql.Date;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.thinkgsp.jxc.model.Bill;
import com.thinkgsp.jxc.model.BillDetail;
import com.thinkgsp.jxc.model.BillExtend;
import com.thinkgsp.jxc.model.Inventory;
import com.thinkgsp.jxc.model.OtherStockInBill;
import com.thinkgsp.jxc.model.OtherStockOutBill;
import com.thinkgsp.jxc.model.ProductContact;
import com.thinkgsp.jxc.model.ProductTransferBill;
import com.thinkgsp.jxc.model.PurchaseStockInBill;
import com.thinkgsp.jxc.model.RejectStockInBill;
import com.thinkgsp.jxc.model.RejectStockOutBill;
import com.thinkgsp.jxc.model.RpAPayBill;
import com.thinkgsp.jxc.model.RpARecBill;
import com.thinkgsp.jxc.model.RpArpBill;
import com.thinkgsp.jxc.model.SalesPriceDiffBill;
import com.thinkgsp.jxc.model.SalesStockOutBill;
import com.thinkgsp.jxc.model.StockInBill;
import com.thinkgsp.jxc.model.StockOutBill;
import com.thinkgsp.jxc.model.StockTransferBill;
import com.thinkgsp.jxc.persistence.BillMapper;
import com.thinkgsp.jxc.persistence.InventoryMapper;
import com.thinkgsp.jxc.persistence.ProductContactMapper;
import com.thinkgsp.jxc.persistence.mybatis.PagingResult;
import com.thinkgsp.jxc.service.BillService;
import com.thinkgsp.jxc.service.RpArpBillService;
import com.thinkgsp.jxc.service.ServiceException;
import com.thinkgsp.sys.model.SysAccountPeriod;

/**
 * @author evan
 *
 */
@Service("billService")
@Transactional
public class BillServiceImpl extends GeneralServiceImpl implements BillService {

	@Autowired
	private BillMapper billMapper;
	@Autowired
	private ProductContactMapper pcMapper;
	@Autowired
	private InventoryMapper invMapper;
	@Autowired
	private RpArpBillService arpBillService;
	
	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.BillService#getBill(java.lang.Long)
	 */
	public Bill getBill(Long billId) {
		return billMapper.getBill(billId);
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.BillService#findPurchaseStockInBill(java.sql.Date, java.sql.Date)
	 */
	public List<PurchaseStockInBill> findPurchaseStockInBill(Date begDate,
			Date endDate,Integer custId) {
		List<Integer> billTypes = new ArrayList<Integer>(1);
		billTypes.add(1);
		List<PurchaseStockInBill> bills = billMapper.getBillList(billTypes, begDate, endDate,custId);
		return bills;
	}

	public PagingResult<PurchaseStockInBill> findPurchaseStockInBill(
			Date begDate, Date endDate, Integer custId, int start, int size) {
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("begDate", begDate);
		params.put("endDate", endDate);
		params.put("custId", custId);
		List<Integer> billTypes = new ArrayList<Integer>(1);
		billTypes.add(1);
		params.put("billTypes", billTypes);
		RowBounds rows = new RowBounds(start, size);
		return queryPagingHandler.queryPagingResult(
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListCount", 
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListPaging", params, rows);
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.BillService#findOtherStockInBill(java.sql.Date, java.sql.Date)
	 */
	public List<OtherStockInBill> findOtherStockInBill(Date begDate,
			Date endDate) {
		List<Integer> billTypes = new ArrayList<Integer>(1);
		billTypes.add(3);
		List<OtherStockInBill> bills = billMapper.getBillList(billTypes, begDate, endDate,null);
		return bills;
	}

	public PagingResult<OtherStockInBill> findOtherStockInBill(Date begDate,
			Date endDate, int start, int size) {
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("begDate", begDate);
		params.put("endDate", endDate);
		List<Integer> billTypes = new ArrayList<Integer>(1);
		billTypes.add(3);
		params.put("billTypes", billTypes);
		RowBounds rows = new RowBounds(start, size);
		return queryPagingHandler.queryPagingResult(
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListCount", 
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListPaging", params, rows);
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.BillService#findRejectStockInBill(java.sql.Date, java.sql.Date)
	 */
	public List<RejectStockInBill> findRejectStockInBill(Date begDate,
			Date endDate,Integer custId) {
		List<Integer> billTypes = new ArrayList<Integer>(1);
		billTypes.add(2);
		List<RejectStockInBill> bills = billMapper.getBillList(billTypes, begDate, endDate,custId);
		return bills;
	}

	public PagingResult<RejectStockInBill> findRejectStockInBill(Date begDate,
			Date endDate, Integer custId, int start, int size) {
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("begDate", begDate);
		params.put("endDate", endDate);
		params.put("custId", custId);
		List<Integer> billTypes = new ArrayList<Integer>(1);
		billTypes.add(2);
		params.put("billTypes", billTypes);
		RowBounds rows = new RowBounds(start, size);
		return queryPagingHandler.queryPagingResult(
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListCount", 
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListPaging", params, rows);
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.BillService#findStockInBill(java.sql.Date, java.sql.Date)
	 */
	public List<StockInBill> findStockInBill(Date begDate, Date endDate) {
		List<Integer> billTypes = new ArrayList<Integer>(3);
		billTypes.add(1);
		billTypes.add(2);
		billTypes.add(3);
		List<StockInBill> bills = billMapper.getBillList(billTypes, begDate, endDate,null);
		return bills;
	}

	public PagingResult<StockInBill> findStockInBill(Date begDate,
			Date endDate, int start, int size) {
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("begDate", begDate);
		params.put("endDate", endDate);
		List<Integer> billTypes = new ArrayList<Integer>(1);
		billTypes.add(1);
		billTypes.add(2);
		billTypes.add(3);
		params.put("billTypes", billTypes);
		RowBounds rows = new RowBounds(start, size);
		return queryPagingHandler.queryPagingResult(
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListCount", 
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListPaging", params, rows);
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.BillService#findSalesStockOutBill(java.sql.Date, java.sql.Date)
	 */
	public List<SalesStockOutBill> findSalesStockOutBill(Date begDate,
			Date endDate,Integer custId) {
		List<Integer> billTypes = new ArrayList<Integer>(1);
		billTypes.add(-1);
		List<SalesStockOutBill> bills = billMapper.getBillList(billTypes, begDate, endDate,custId);
		return bills;
	}

	public PagingResult<SalesStockOutBill> findSalesStockOutBill(Date begDate,
			Date endDate, Integer custId, int start, int size) {
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("begDate", begDate);
		params.put("endDate", endDate);
		params.put("custId", custId);
		List<Integer> billTypes = new ArrayList<Integer>(1);
		billTypes.add(-1);
		params.put("billTypes", billTypes);
		RowBounds rows = new RowBounds(start, size);
		return queryPagingHandler.queryPagingResult(
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListCount", 
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListPaging", params, rows);
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.BillService#findOtherStockOutBill(java.sql.Date, java.sql.Date)
	 */
	public List<OtherStockOutBill> findOtherStockOutBill(Date begDate,
			Date endDate) {
		List<Integer> billTypes = new ArrayList<Integer>(1);
		billTypes.add(-3);
		List<OtherStockOutBill> bills = billMapper.getBillList(billTypes, begDate, endDate,null);
		return bills;
	}

	public PagingResult<OtherStockOutBill> findOtherStockOutBill(Date begDate,
			Date endDate, int start, int size) {
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("begDate", begDate);
		params.put("endDate", endDate);
		List<Integer> billTypes = new ArrayList<Integer>(1);
		billTypes.add(-3);
		params.put("billTypes", billTypes);
		RowBounds rows = new RowBounds(start, size);
		return queryPagingHandler.queryPagingResult(
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListCount", 
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListPaging", params, rows);
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.BillService#findRejectStockOutBill(java.sql.Date, java.sql.Date)
	 */
	public List<RejectStockOutBill> findRejectStockOutBill(Date begDate,
			Date endDate,Integer custId) {
		List<Integer> billTypes = new ArrayList<Integer>(1);
		billTypes.add(-2);
		List<RejectStockOutBill> bills = billMapper.getBillList(billTypes, begDate, endDate,custId);
		return bills;
	}

	public PagingResult<RejectStockOutBill> findRejectStockOutBill(
			Date begDate, Date endDate, Integer custId, int start, int size) {
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("begDate", begDate);
		params.put("endDate", endDate);
		params.put("custId", custId);
		List<Integer> billTypes = new ArrayList<Integer>(1);
		billTypes.add(-2);
		params.put("billTypes", billTypes);
		RowBounds rows = new RowBounds(start, size);
		return queryPagingHandler.queryPagingResult(
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListCount", 
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListPaging", params, rows);
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.BillService#findStockOutBill(java.sql.Date, java.sql.Date)
	 */
	public List<StockOutBill> findStockOutBill(Date begDate, Date endDate) {
		List<Integer> billTypes = new ArrayList<Integer>(3);
		billTypes.add(-1);
		billTypes.add(-2);
		billTypes.add(-3);
		List<StockOutBill> bills = billMapper.getBillList(billTypes, begDate, endDate,null);
		return bills;
	}

	public PagingResult<StockOutBill> findStockOutBill(Date begDate,
			Date endDate, int start, int size) {
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("begDate", begDate);
		params.put("endDate", endDate);
		List<Integer> billTypes = new ArrayList<Integer>(3);
		billTypes.add(-1);
		billTypes.add(-2);
		billTypes.add(-3);
		params.put("billTypes", billTypes);
		RowBounds rows = new RowBounds(start, size);
		return queryPagingHandler.queryPagingResult(
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListCount", 
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListPaging", params, rows);
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.BillService#findStockTransferBill(java.sql.Date, java.sql.Date)
	 */
	public List<StockTransferBill> findStockTransferBill(Date begDate,
			Date endDate) {
		List<Integer> billTypes = new ArrayList<Integer>(1);
		billTypes.add(0);
		List<StockTransferBill> bills = billMapper.getBillList(billTypes, begDate, endDate,null);
		return bills;
	}

	public PagingResult<StockTransferBill> findStockTransferBill(Date begDate,
			Date endDate, int start, int size) {
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("begDate", begDate);
		params.put("endDate", endDate);
		List<Integer> billTypes = new ArrayList<Integer>(1);
		billTypes.add(0);
		params.put("billTypes", billTypes);
		RowBounds rows = new RowBounds(start, size);
		return queryPagingHandler.queryPagingResult(
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListCount", 
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListPaging", params, rows);
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.BillService#findProductTransferBill(java.sql.Date, java.sql.Date)
	 */
	public List<ProductTransferBill> findProductTransferBill(Date begDate,
			Date endDate) {
		List<Integer> billTypes = new ArrayList<Integer>(1);
		billTypes.add(101);
		List<ProductTransferBill> bills = billMapper.getBillList(billTypes, begDate, endDate,null);
		return bills;
	}

	public PagingResult<ProductTransferBill> findProductTransferBill(
			Date begDate, Date endDate, int start, int size) {
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("begDate", begDate);
		params.put("endDate", endDate);
		List<Integer> billTypes = new ArrayList<Integer>(1);
		billTypes.add(101);
		params.put("billTypes", billTypes);
		RowBounds rows = new RowBounds(start, size);
		return queryPagingHandler.queryPagingResult(
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListCount", 
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListPaging", params, rows);
	}
	
	public List<SalesPriceDiffBill> findSalesPriceDiffBill(Date begDate,
			Date endDate,Integer custId) {
		List<Integer> billTypes = new ArrayList<Integer>(1);
		billTypes.add(-4);
		List<SalesPriceDiffBill> bills = billMapper.getBillList(billTypes, begDate, endDate, custId);
		return bills;
	}

	public PagingResult<SalesPriceDiffBill> findSalesPriceDiffBill(
			Date begDate, Date endDate,Integer custId, int start, int size) {
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("begDate", begDate);
		params.put("endDate", endDate);
		params.put("custId", custId);
		List<Integer> billTypes = new ArrayList<Integer>(1);
		billTypes.add(-4);
		params.put("billTypes", billTypes);
		RowBounds rows = new RowBounds(start, size);
		return queryPagingHandler.queryPagingResult(
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListCount", 
				"com.thinkgsp.jxc.persistence.BillMapper.getBillListPaging", params, rows);
	}
	

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.BillService#saveBill(com.thinkgsp.jxc.model.Bill)
	 */
	public void saveBill(Bill bill) throws ServiceException {
		if(bill.getDetails().size() == 0) {
			throw new ServiceException("单据没有明细,不允许保存。");
		}
		if(bill.getBillId() == null || bill.getBillId() == 0L) {
			billMapper.insertBill(bill);
			for(BillDetail detail : bill.getDetails()) {
				detail.setBillId(bill.getBillId());
				billMapper.insertBillDetail(detail);
			}
			if(bill instanceof StockTransferBill) {
				BillExtend be = bill.getBillExtend();
				be.setBillId(bill.getBillId());
				billMapper.insertBillExtend(be);
			}
		} else {
			if(bill.getArchived()) {
				throw new ServiceException("单据已经归档，不能修改。");
			} else {
				Bill newBill = billMapper.getBill(bill.getBillId());
				if(newBill.getArchived()) {
					throw new ServiceException("单据已经归档，不能修改。");
				}
			}
			billMapper.updateBill(bill);
			billMapper.deleteBillDetailNotIn(bill.getBillId(), bill.getDetails());
			for(BillDetail detail : bill.getDetails()) {
				if(detail.getId() == null || detail.getId() == 0L) {
					billMapper.insertBillDetail(detail);
				} else {
					billMapper.updateBillDetail(detail);
				}
			}
			if(bill instanceof StockTransferBill) {
				billMapper.updateBillExtend(bill.getBillExtend());
			}
		}
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.BillService#deleteBill(java.lang.Long)
	 */
	public void deleteBill(Long billId) throws ServiceException {
		Bill bill = billMapper.getBill(billId);
		if(bill.getArchived()) {
			throw new ServiceException("单据已经归档，不能删除。");
		}
		Boolean hasRecord = pcMapper.hasRecord(billId);
		if(hasRecord) {
			billMapper.updateDeleted(billId, true);
		} else {
			billMapper.deleteBillExtend(billId);
			billMapper.deleteBillDetailByBillId(billId);
			billMapper.deleteBill(billId);
		}
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.BillService#archiveBill(com.thinkgsp.jxc.model.Bill)
	 */
	public void archiveBill(Bill bill) throws ServiceException {
		archiveBill(bill.getBillId());
	}
	

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.BillService#unArchiveBill(com.thinkgsp.jxc.model.Bill)
	 */
	public void unArchiveBill(Bill bill) throws ServiceException {
		unArchiveBill(bill.getBillId());
	}

	public void setBillMapper(BillMapper billMapper) {
		this.billMapper = billMapper;
	}

	public void setProductContactMapper(ProductContactMapper pcMapper) {
		this.pcMapper = pcMapper;
	}

	public void setInventoryMapper(InventoryMapper invMapper) {
		this.invMapper = invMapper;
	}

	public void archiveBill(Long billId) throws ServiceException {
		Bill bill = billMapper.getBill(billId);
		if(bill.getArchived()) {
			throw new ServiceException("单据已经归档，不能再次归档。");
		}
		if(bill.getDeleted()) {
			throw new ServiceException("单据已经删除，不能归档。");
		}
		SysAccountPeriod ap = periodMapper.getCurrentPeriod();
		if(!bill.getFyear().equals(ap.getFyear()) || 
				!bill.getPeriod().equals(ap.getPeriod())) {
			throw new ServiceException("不是本期单据，不允许归档。");
		}
		BillExtend be = bill.getBillExtend();
		for(BillDetail detail : bill.getDetails()) {
			int direct = bill.getDirect().intValue() + detail.getDirect().intValue();
			if(direct == 1) {
				income(bill,detail,be);
			} else if (direct == -1) {
				outcome(bill,detail,be);
			} else if (direct == 0) {
				income(bill,detail,be);
				outcome(bill,detail,be);
			} 
			//添加销售差价单，direct=-2
//			else {
//				throw new ServiceException("单据'" + bill.getBillCode() + "'的出入库方向无效:" + direct);
//			}
		}
		
		billMapper.archiveBill(billId, true);
		
		int direct = bill.getDirect().intValue();
		if(direct > 0) {
			arpBillService.createAPayBillFromStockInBill(bill.getBillId());
		} else if(direct < 0) {
			arpBillService.createARecBillFromStockOutBill(bill.getBillId());
		}
	}

	public void unArchiveBill(Long billId) throws ServiceException {
		Bill bill = billMapper.getBill(billId);
		if(!bill.getArchived()) {
			throw new ServiceException("单据未归档，不能反归档。");
		}
		if(bill.getDeleted()) {
			throw new ServiceException("单据已经删除，不能反归档。");
		}
		SysAccountPeriod ap = periodMapper.getCurrentPeriod();
		if(bill.getFyear().intValue() != ap.getFyear().intValue() || 
				bill.getPeriod().intValue() != ap.getPeriod().intValue()) {
			throw new ServiceException("不是本期单据，不允许反归档。");
		}
		try {
			// 开始反归档应收应付单与删除应收应付单
			RpArpBill arpBill = arpBillService.getArpBillFromStockBill(billId);
			if(arpBill.getCheckStatus() == 1 || 
					BigDecimal.ZERO.compareTo(arpBill.getCheckedAmount()) == -1) {
				
				throw new ServiceException("本单据关联的" + arpBill.getArpBillName() + "已经结算，不能反归档。");
			}
			
			if(arpBill instanceof RpARecBill) {
				arpBillService.unArchiveARecBill(arpBill.getArpBillId());
			} else if(arpBill instanceof RpAPayBill) {
				arpBillService.unArchiveAPayBill(arpBill.getArpBillId());
			} else {
				throw new ServiceException("应收应付类型无效,无法反归档单据。\n billType:" + arpBill.getClass().getName());
			}
		
			arpBillService.deleteArpBill(arpBill.getArpBillId());
			// 应收应付单删除结束
			
			BillExtend be = bill.getBillExtend();
			for(BillDetail detail : bill.getDetails()) {
				int direct = bill.getDirect().intValue() + detail.getDirect().intValue();
				if(direct == 1) {
					unIncome(bill, detail, be);
				} else if(direct == -1) {
					unOutcome(bill, detail, be);
				} else if(direct == 0) {
					unIncome(bill, detail, be);
					unOutcome(bill, detail, be);
				}
			}
			billMapper.archiveBill(billId, false);
		} catch(ServiceException e) {
			log.error(e);
			throw e;
		} catch(Exception e) {
			log.error(e);
			throw new ServiceException(e.getMessage());
		}
	}
	
	/**
	 * 创建商品往来对象
	 * 每个单据的商品执行时，单据内的每个商品都需要创建出入库流水，并记录到数据库中
	 * @param bill 库存单据
	 * @param detail 单据明细
	 * @param be 单据扩展
	 * @param inv 单据明细对应的库存条目，入库方法可以没有库存条目，但是反入库操作必须要关联如果条目
	 * @return
	 */
	private ProductContact createProductContactForOutcome(Bill bill,BillDetail detail,BillExtend be,Inventory inv,boolean isUnOutcome) {
		ProductContact pc = new ProductContact();
		pc.setInvAddId(inv.getInvAddId());
		pc.setProductId(inv.getProductId());
		pc.setBatchNoId(inv.getBatchNoId());
		pc.setBillId(bill.getBillId());
		pc.setBillDetailId(detail.getId());
		pc.setBillDate(bill.getBillDate());
		pc.setCustomerId(bill.getCustId());
		String summary = null;
		if(isUnOutcome) {
			summary = "反出库";
		} else {
			if(bill instanceof SalesStockOutBill) {
				summary = "产品销售";
			} else if(bill instanceof RejectStockOutBill) {
				summary = "销售退回";
			} else {
				summary = "产品出库";
			}			
		}
		pc.setSummary(summary);
		pc.setFyear(bill.getFyear());
		pc.setPeriod(bill.getPeriod());
		pc.setDirect(-1);
		pc.setInQty(BigDecimal.ZERO);
		pc.setInAmt(BigDecimal.ZERO);
		BigDecimal outAmt = BigDecimal.ZERO;
		if(isUnOutcome) {
			pc.setOutQty(detail.getQuantity().negate());
			outAmt = detail.getQuantity().negate().multiply(inv.getCostPrice());
		} else {
			pc.setOutQty(detail.getQuantity());
			outAmt = detail.getQuantity().multiply(inv.getCostPrice());
		}
		pc.setOutAmt(outAmt);
		pc.setRemQty(inv.getRemainQty().subtract(pc.getOutQty()));
		pc.setRemAmt(inv.getRemainAmt().subtract(outAmt));
		return pc;
	}
	
	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.serviceImpl.BillServiceImpl#createProductContactForOutcome(Bill ,BillDetail ,BillExtend ,Inventory )
	 * @param bill
	 * @param detail
	 * @param be
	 * @param inv
	 * @return
	 */
	private ProductContact createProductContactForIncome(Bill bill,BillDetail detail,BillExtend be,Inventory inv,boolean isUnIncome) {
		ProductContact pc = new ProductContact();
		pc.setInvAddId(inv.getInvAddId());
		pc.setProductId(inv.getProductId());
		pc.setBatchNoId(inv.getBatchNoId());
		pc.setBillId(bill.getBillId());
		pc.setBillDetailId(detail.getId());
		pc.setBillDate(bill.getBillDate());
		pc.setCustomerId(bill.getCustId());
		String summary = null;
		if(isUnIncome) {
			summary = "反入库";
		} else {
			if(bill instanceof PurchaseStockInBill) {
				summary = "产品购进";
			} else if(bill instanceof RejectStockInBill) {
				summary = "购进退出";
			} else {
				summary = "产品入库";
			}
		}
		pc.setSummary(summary);
		pc.setFyear(bill.getFyear());
		pc.setPeriod(bill.getPeriod());
		pc.setDirect(1);
		BigDecimal inAmt = BigDecimal.ZERO;
		if(isUnIncome) {
			pc.setInQty(detail.getQuantity().negate());
			inAmt = detail.getQuantity().negate().multiply(detail.getSettlePrice());
		} else {
			pc.setInQty(detail.getQuantity());
			inAmt = detail.getQuantity().multiply(detail.getSettlePrice());
		}
		pc.setInAmt(inAmt);
		pc.setOutQty(BigDecimal.ZERO);
		pc.setOutAmt(BigDecimal.ZERO);
		pc.setRemQty(inv.getRemainQty().add(pc.getInQty()));
		pc.setRemAmt(inv.getRemainAmt().add(inAmt));
		return pc;		
	}
	
	/**
	 * 入库操作
	 * @param bill
	 * @param detail
	 * @param be
	 * @throws ServiceException 如果单据的出入库方向无效，或者转仓单的调入或调出地址为空则抛出异常
	 */
	private void income(Bill bill,BillDetail detail,BillExtend be) throws ServiceException {
		int direct = bill.getDirect().intValue() + detail.getDirect().intValue();
		Integer invAddId = null;
		if(direct == 1) {
			invAddId = detail.getInvAddId();
		} else if(direct == 0) {
			invAddId = be.getTransferIn();
		} else {
			String msg = "单据:'" + bill.getBillId() + "'中的商品:'" + detail.getProduct().getProductName() + "'入库方向不确定。";
			log.warn(msg);
			throw new ServiceException(msg);
		}
		if(invAddId == null || invAddId == 0) {
			String msg = "单据:'" + bill.getBillId() + "'中的商品:'" + detail.getProduct().getProductName() + "'入库方不确定。";
			log.warn(msg);
			throw new ServiceException(msg);
		}
		SysAccountPeriod period = this.periodMapper.getCurrentPeriod();
		Inventory inv = invMapper.findInventory(period.getFyear(), period.getPeriod(), 
				detail.getProductId(), invAddId, detail.getBatchNoId());
		//商品入库时，当未检索到库存条目时需要创建一个
		if(inv == null) {
			inv = new Inventory();
			inv.setBatchNoId(detail.getBatchNoId());
			inv.setInvAddId(invAddId);
			inv.setProductId(detail.getProductId());
			inv.setFyear(period.getFyear());
			inv.setPeriod(period.getPeriod());
			inv.setCostPrice(detail.getSettlePrice());
			invMapper.insertInventory(inv);
			inv = invMapper.findInventory(period.getFyear(), period.getPeriod(), detail.getProductId(), invAddId, detail.getBatchNoId());
		}
		ProductContact pc = createProductContactForIncome(bill, detail, be, inv,false);
		pcMapper.recordIncome(pc);
		invMapper.income(inv.getId(), pc.getInQty(), pc.getInAmt());
		invMapper.updateCostPrice(inv.getId());
	}
	
	/**
	 * 单据反入库操作
	 * @param bill
	 * @param detail
	 * @param be
	 * @throws ServiceException 如果单据出入库方向无效或者无法关联库存条目则抛出异常
	 */
	private void unIncome(Bill bill,BillDetail detail,BillExtend be) throws ServiceException {
		int direct = bill.getDirect().intValue() + detail.getDirect().intValue();
		Integer invAddId = null;
		if(direct == 1) {
			invAddId = detail.getInvAddId();
		} else if(direct == 0) {
			invAddId = be.getTransferIn();
		} else {
			String msg = "单据:'" + bill.getBillId() + "'中的商品:'" + detail.getProduct().getProductName() + "'入库方向不确定。";
			log.warn(msg + " bill.direct=" + bill.getDirect() + ";detail.direct=" + detail.getDirect());
			throw new ServiceException(msg);			
		}
		if(invAddId == null || invAddId == 0) {
			String msg = "单据:'" + bill.getBillId() + "'中的商品:'" + detail.getProduct().getProductName() + "'入库方不确定。";
			log.warn(msg);
			throw new ServiceException(msg);
		}
		SysAccountPeriod period = this.periodMapper.getCurrentPeriod();
		Inventory inv = invMapper.findInventory(period.getFyear(), period.getPeriod(), 
				detail.getProductId(), invAddId, detail.getBatchNoId());
		if(inv == null) {
			throw new ServiceException("无法检索到商品:" + detail.getProduct().getProductName() + "'的库存记录。");
		}
		ProductContact pc = createProductContactForIncome(bill, detail, be, inv,true);
		pcMapper.recordUnIncome(pc);
		invMapper.income(inv.getId(), pc.getInQty(), pc.getInAmt());
		invMapper.updateCostPrice(inv.getId());
	}
	
	/**
	 * 出库操作
	 * @param bill
	 * @param detail
	 * @param be
	 * @throws ServiceException 如果单据出入库方向无效或者无法关联库存条目则抛出异常
	 */
	private void outcome(Bill bill,BillDetail detail,BillExtend be) throws ServiceException {
		int direct = bill.getDirect().intValue() + detail.getDirect().intValue();
		Integer invAddId = null;
		if(direct == -1) {
			invAddId = detail.getInvAddId();
		} else if(direct == 0) {
			invAddId = be.getTransferOut();
		} else {
			String msg = "单据:'" + bill.getBillId() + "'中的商品:'" + detail.getProduct().getProductName() + "'出库方向不确定。";
			log.warn(msg);
			throw new ServiceException(msg);			
		}
		if(invAddId == null || invAddId == 0) {
			String msg = "单据:'" + bill.getBillId() + "'中的商品:'" + detail.getProduct().getProductName() + "'出库方不确定。";
			log.warn(msg);
			throw new ServiceException(msg);
		}
		SysAccountPeriod period = periodMapper.getCurrentPeriod();
		Inventory inv = invMapper.findInventory(period.getFyear(), period.getPeriod(), detail.getProductId(),
										invAddId, detail.getBatchNoId());
		if(inv == null) {
			throw new ServiceException("无法检索到商品:" + detail.getProduct().getProductName() + "'的库存记录。");
		}
		ProductContact pc = createProductContactForOutcome(bill, detail, be, inv,false);
		pcMapper.recordOutcome(pc);
		invMapper.outcome(inv.getId(), pc.getOutQty());
		billMapper.updateBillDetailCostPrice(detail.getId(), inv.getCostPrice());
		detail.setCostPrice(inv.getCostPrice());
	}
	
	/**
	 * 反出库操作
	 * @param bill
	 * @param detail
	 * @param be
	 * @throws ServiceException 如果单据出入库方向无效或者无法关联库存条目则抛出异常
	 */
	private void unOutcome(Bill bill,BillDetail detail,BillExtend be) throws ServiceException {
		int direct = bill.getDirect().intValue() + detail.getDirect().intValue();
		Integer invAddId = null;
		if(direct == -1) {
			invAddId = detail.getInvAddId();
		} else if(direct == 0) {
			invAddId = be.getTransferOut();
		} else {
			String msg = "单据:'" + bill.getBillId() + "'中的商品:'" + detail.getProduct().getProductName() + "'出库方向不确定。";
			log.warn(msg);
			throw new ServiceException(msg);			
		}
		if(invAddId == null || invAddId == 0) {
			String msg = "单据:'" + bill.getBillId() + "'中的商品:'" + detail.getProduct().getProductName() + "'出库方不确定。";
			log.warn(msg);
			throw new ServiceException(msg);
		}
		SysAccountPeriod period = periodMapper.getCurrentPeriod();
		Inventory inv = invMapper.findInventory(period.getFyear(), period.getPeriod(), detail.getProductId(),
										invAddId, detail.getBatchNoId());
		if(inv == null) {
			throw new ServiceException("无法检索到商品:" + detail.getProduct().getProductName() + "'的库存记录。");
		}
		ProductContact pc = createProductContactForOutcome(bill, detail, be, inv,true);
		pcMapper.recordUnOutcome(pc);
		invMapper.outcome(inv.getId(), pc.getOutQty());
	}

	public String newNumber(@SuppressWarnings("rawtypes") Class clazz) throws ServiceException {
		int roleId = 0;
		if(PurchaseStockInBill.class.equals(clazz)) {
			roleId = numberRoleConfig.getPurchaseStockInBillRoleId();
		} else if(SalesStockOutBill.class.equals(clazz)) {
			roleId = numberRoleConfig.getSalesStockOutBillRoleId();
		} else if(OtherStockInBill.class.equals(clazz)) {
			roleId = numberRoleConfig.getOtherStockInBillRoleId();
		} else if(OtherStockOutBill.class.equals(clazz)) {
			roleId = numberRoleConfig.getOtherStockOutBillRoleId();
		} else {
			throw new ServiceException("无效的单据类型:" + clazz.getName());
		}
		return numberRoleService.newNumber(roleId);
	}


}
